SHELL := powershell.exe

TINYGO ?= tinygo
NASM ?= nasm
LD ?= ld.lld
OBJCOPY ?= llvm-objcopy
QEMU ?= qemu-system-x86_64

BUILD_DIR := build
KERNEL_OBJ := $(BUILD_DIR)/kernel.o
KERNEL_ENTRY_OBJ := $(BUILD_DIR)/kernel_entry.o
PORT_IO_OBJ := $(BUILD_DIR)/port_io.o
TRAMP_OBJ := $(BUILD_DIR)/trampoline.o
INTERRUPTS_OBJ := $(BUILD_DIR)/interrupts.o
CONTEXT_OBJ := $(BUILD_DIR)/context.o
KERNEL_ELF := $(BUILD_DIR)/kernel.elf
KERNEL_BIN := $(BUILD_DIR)/kernel.bin
KERNEL_PAD := $(BUILD_DIR)/kernel.padded.bin
SECTORS_FILE := $(BUILD_DIR)/kernel.sectors
BOOT_BIN := $(BUILD_DIR)/boot.bin
OS_IMG := $(BUILD_DIR)/os.img

TARGET := targets/tinygo-i386.json
LINKER := linker.ld

.PHONY: all build run clean ensure-dirs

all: build

ensure-dirs:
	@if (!(Test-Path $(BUILD_DIR))) { New-Item -ItemType Directory $(BUILD_DIR) | Out-Null }

$(KERNEL_OBJ): cmd/kernel/main.go pkg/vga/vga.go | ensure-dirs
	$(TINYGO) build -o $(KERNEL_OBJ) -target $(TARGET) -scheduler none -panic trap -no-debug ./cmd/kernel

$(KERNEL_ENTRY_OBJ): boot/kernel_entry.asm | ensure-dirs
	$(NASM) -f elf32 -o $(KERNEL_ENTRY_OBJ) boot/kernel_entry.asm

$(PORT_IO_OBJ): boot/port_io.asm | ensure-dirs
	$(NASM) -f elf32 -o $(PORT_IO_OBJ) boot/port_io.asm

$(TRAMP_OBJ): boot/trampoline.asm | ensure-dirs
	$(NASM) -f elf32 -o $(TRAMP_OBJ) boot/trampoline.asm

$(INTERRUPTS_OBJ): boot/interrupts.asm | ensure-dirs
	$(NASM) -f elf32 -o $(INTERRUPTS_OBJ) boot/interrupts.asm

$(CONTEXT_OBJ): boot/context.asm | ensure-dirs
	$(NASM) -f elf32 -o $(CONTEXT_OBJ) boot/context.asm

$(KERNEL_ELF): $(KERNEL_OBJ) $(KERNEL_ENTRY_OBJ) $(PORT_IO_OBJ) $(TRAMP_OBJ) $(INTERRUPTS_OBJ) $(LINKER) | ensure-dirs
	$(LD) -m elf_i386 -T $(LINKER) -o $(KERNEL_ELF) $(KERNEL_ENTRY_OBJ) $(PORT_IO_OBJ) $(TRAMP_OBJ) $(INTERRUPTS_OBJ) $(CONTEXT_OBJ) $(KERNEL_OBJ)

$(KERNEL_BIN): $(KERNEL_ELF) | ensure-dirs
	$(OBJCOPY) -O binary $(KERNEL_ELF) $(KERNEL_BIN)

$(KERNEL_PAD): $(KERNEL_BIN) | ensure-dirs
	$bytes = [System.IO.File]::ReadAllBytes("$(KERNEL_BIN)"); \
	$padding = (512 - ($bytes.Length % 512)) % 512; \
	$list = New-Object System.Collections.Generic.List[byte]; \
	$list.AddRange($bytes); \
	if ($padding -gt 0) { \
		$pad = New-Object byte[] $padding; \
		$list.AddRange($pad); \
	} \
	[System.IO.File]::WriteAllBytes("$(KERNEL_PAD)", $list.ToArray())

$(SECTORS_FILE): $(KERNEL_PAD) | ensure-dirs
	$size = (Get-Item $(KERNEL_PAD)).Length; \
	$sectors = [int][Math]::Ceiling($size / 512.0); \
	Set-Content $(SECTORS_FILE) -Value $sectors

$(BOOT_BIN): boot/boot.asm $(SECTORS_FILE) | ensure-dirs
	$sectors = Get-Content $(SECTORS_FILE); \
	$(NASM) -f bin -o $(BOOT_BIN) -D KERNEL_SECTORS=$sectors boot/boot.asm

$(OS_IMG): $(BOOT_BIN) $(KERNEL_PAD) | ensure-dirs
	cmd /c copy /b "$(BOOT_BIN)"+"$(KERNEL_PAD)" "$(OS_IMG)" > NUL

build: $(OS_IMG)

run: $(OS_IMG)
	$(QEMU) -drive format=raw,file=$(OS_IMG) -serial stdio -display curses

clean:
	@if (Test-Path $(BUILD_DIR)) { Remove-Item -Recurse -Force $(BUILD_DIR) }
